6.16 The BeanFactory

BeanFactory提供了Spring IOC 功能的基础,但它仅仅被直接使用在于其他第三方框架的集成中,现在对于大部分的Spring用户来说,那是太长远的事了。BeanFactory和它关联的接口,例如BeanFactoryAware,InitializingBean,DisposableBean,现在仍然存在Spring中,是为了向后兼容与大量的第三方框架集成。通常第三方组件不使用更新的替代物如@PostConstruct@PreDestory,以便保留与JDK1.4的兼容性或避免对JSR-250的依赖。

这个章节提供了BeanFactoryApplicationContext额外的背景差异以及如何直接访问IOC容器通过一个典型的单例查找。

6.16.1 BeanFactory or ApplicationContext?

除非你有更好的理由,否则尽量使用ApplicationContext

ApplicationContext包含了BeanFactory的所有功能。通常建议比BeanFactory优先,除非有一些限制的场合如字节长度对内存有很大的影响时(Applet)。当然,对于绝大多数"典型的"企业应用系统,ApplicationContext就是你需要使用的。Spring大量使用了BeanPostProcessor扩展(以便应用代理等功能)。如果你仅仅使用简单的BeanFactory,则无法使用相当多的支持功能,如事务和AOP。这可能会导致混乱,因为配置并没有错误。

下面的列表列出了BeanFactory接口和ApplicationContext接口提供的特性和实现。 表 6.9. Feature Matrix特性表

特性 BeanFactory ApplicationContext
Bean 实例化/装配 Yes Yes
自动 BeanPostProcessor 注册 No Yes
自动 BeanFactoryPostProcessor 注册 No Yes
便捷的 MessageSource 访问( i18n) No Yes
ApplicationEvent 发送 No Yes

为了给BeanFactory实现明确地注册一个bean后置处理器,你必须这样写代码:

ConfigurableBeanFactory factory = new XmlBeanFactory(...);

// now register any needed BeanPostProcessor instances
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(postProcessor);

// now start using the factory

为了给BeanFactory实现明确地注册一个BeanFactoryPostProcessor,你必须这样写代码:

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));

// bring in some property values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));

// now actually do the replacement
cfg.postProcessBeanFactory(factory);

这2个例子中,明确的注册步骤使用不是很方便,这就是为什么各种ApplicationContext实现者相对于简单的BeanFactory是更好的选择,特别是当使用BeanFactoryPostProcessorBeanPostProcessors的时候。这些机制实现了重要的功能,例如属性的占位符替换和AOP。

粘合代码和可怕的singleton

一个应用中的大多数代码最好写成依赖注入(控制反转)的风格,这样代码就和Spring IoC容器无关,它们在被创建时从容器得到自己的依赖,并且完全不知道容器的存在。然而,对于少量需要与其它代码粘合的粘合层代码来说,有时候就需要以一种singleton(或者类似singleton)的方式来访问Spring IoC容器。例如,第三方的代码可能试图(以Class.forName()的方式)直接构造一个新的对象,但无法强制它们从Spring IoC容器中得到这些对象。如果第三方代码构造的对象只是一个小stub或proxy,并且使用singleton方式访问Spring IoC容器来获得真正的对象,那么大多数的代码(由容器产生的对象)仍然可以使用控制反转。因此大多数的代码依然不需要知道容器的存在,或者它如何被访问,并保持与其它代码的解耦,这样所带来的益处是很显然的。EJB也可以使用这种stub/proxy方案代理到由Spring IoC容器产生的普通的Java实现对象。虽然理想情况下Spring IoC容器不需要是singleton,但是如果每个bean使用它自己的non-singleton的Spring IoC容器(当在Spring IoC容器中使用bean时,如Hibernate SessionFactory),对于内存使用或初始化次数都是不切实际。

你可以查看SingletonBeanFactoryLocatorContextSingletonBeanFactoryLocator的JavaDoc来获得详细的例子。正如在EJB那章所提到的,Spring为EJB提供方便使用的基类,通常使用一个non-singleton的BeanFactoryLocator实现,这样在需要时就可以很容易地被SingletonBeanFactoryLocator和ContextSingletonBeanFactoryLocator替换。